#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>

#include <event.h>
#include <event2/listener.h>


#include "log.h"
#include "logic.h"
#include "socketinfo.h"

#include "server_config.h"

#define UNUSED(x) ((void)(x))

char config_file[1024] = {0};

static int socktoaddr(const char* host, unsigned short port, 
		struct sockaddr_in *addr)
{
	struct hostent *host_ent;
	memset(addr, 0, sizeof(struct sockaddr_in));
	addr->sin_family = AF_INET;
	addr->sin_port   = htons(port);
	//获得主机信息入口
	if ((host_ent=gethostbyname(host))!=NULL) {
		memcpy(&addr->sin_addr, host_ent->h_addr, host_ent->h_length);
	} else {
		//xxx.xxx.xxx.xxx
		if ((addr->sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
			return -1;
		}
	}
	return 0;
}


int setnblock(int iSock)
{
	int iFlags;
	iFlags = fcntl(iSock, F_GETFL, 0);
	iFlags |= O_NONBLOCK;
	iFlags |= O_NDELAY;
	fcntl(iSock, F_SETFL, iFlags);
	return 0;
}

static int setreuse(int socket)
{
	/*将socket设置为重用模式*/
	int flag = 1;
	if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR,
				(char *)&flag, sizeof(flag)) != 0) {
		return -1;
	}
	return 0;
}

static int listen_on(const char * ip, unsigned short port, int backlog)
{
	int fd;
	struct sockaddr_in addr;
	if (socktoaddr(ip, port, &addr) != 0) {
		return -1;
	}

	fd = socket(PF_INET, SOCK_STREAM, 0);
	if (fd == -1) {
		return -1;
	}

	if (setreuse(fd) != 0 || 
			setnblock(fd) != 0 ||
			bind(fd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ||
			listen(fd, backlog) != 0) {
		close(fd);
		return -1;
	}
	return fd;  
}

void on_client_read(struct bufferevent *bev, void *ctx)
{
	struct connection * conn = (struct connection*)ctx;
	WRITE_DEBUG_LOG("socket(id:%llu,fd:%d) on_read", conn->id, conn->fd);
	UNUSED(conn);

	if (logic && logic->on_read) {
		logic->on_read(conn, logic->ctx);
	} else {
		char buff[1024];
		while(bufferevent_read(conn->bev, buff, sizeof(buff)) > 0);
	}
}

void on_client_event(struct bufferevent *bev, short what, void *ctx)
{
	struct connection * conn = (struct connection*)ctx;
	WRITE_DEBUG_LOG("socket(id:%llu,fd:%d) on_event %d",
			conn->id, conn->fd, what);
	
	int need_close = 0;

	if (what & BEV_EVENT_EOF) {
		WRITE_INFO_LOG("socket(id:%llu,fd:%d) closed by peer",
				conn->id, conn->fd);
		need_close = 1;
	} else if (what&BEV_EVENT_ERROR) {
		WRITE_INFO_LOG("socket(id:%llu,fd:%d) closed error:%s",
				conn->id, conn->fd, strerror(errno));
		need_close = 1;
	} else if (what & BEV_EVENT_TIMEOUT) {
		const char * type = "unknown";	
		if (what & BEV_EVENT_READING) {
			type = "read";
		}

		if (what & BEV_EVENT_WRITING) {
			type = "write";
		}

		WRITE_INFO_LOG("socket(id:%llu,fd:%d) %s timeout, closed",
					conn->id, conn->fd, type);
		need_close = 1;
	}

	if (need_close) {
		if (logic && logic->on_closed) {
			logic->on_closed(conn, what, logic->ctx);
		}
		release_connection(conn);	
	}
}

static void on_accept(evutil_socket_t fd, short event, void * p)
{
	WRITE_DEBUG_LOG("on_accept");
	assert(event == EV_READ);

	struct event * ev = (struct event*)p;;
	struct event_base * base = event_get_base(ev);

	int max_recv_count = 0xfffffff;
	int cfg_int = READ_CONFIG_INT("config", "recv_count");
	if (cfg_int > 0) {
		max_recv_count = cfg_int;
	}

	do {
		struct sockaddr_in addr;
		socklen_t addr_len = sizeof(addr);
		int client_fd = accept(fd, (struct sockaddr*)&addr, &addr_len);
		if (client_fd == -1) {
			if (errno == EINTR) continue;
			if (errno == EWOULDBLOCK || errno == EINPROGRESS) {
				return;
			}
			WRITE_INFO_LOG("socket %d accept error: %d-%s",
					fd, errno, strerror(errno));
			event_del(ev);
			free(ev);
			close(fd);
			event_base_loopbreak(base);
			return;
		}

		struct connection * conn =  alloc_connection(base, client_fd);
		WRITE_DEBUG_LOG("socket(id:%llu,fd:%d) connected",
				conn->id, conn->fd);
		bufferevent_setcb(conn->bev,
				on_client_read,
				NULL,
				on_client_event,
				conn);
		bufferevent_enable(conn->bev, EV_READ);

		if (logic && logic->on_accept) {
			logic->on_accept(conn, logic->ctx);
		}
	} while(--max_recv_count);
}

static void on_signal(evutil_socket_t fd, short event, void * p)
{
	if (fd == SIGUSR2) {
		load_server_config(config_file);
		return;
	} else {
		WRITE_INFO_LOG("on_signal %d, exit", event);
		struct event_base * base = (struct event_base*)p;
		event_base_loopbreak(base);
		assert(event == EV_SIGNAL);
	}
}

static int ev_listen_on(struct event_base * base,
		const char * host, int port, int backlog)
{
	int fd = listen_on(host, port, backlog);
	if (fd == -1) {
		WRITE_ERROR_LOG("listen on %s:%d failed: %s",
				host, port, strerror(errno));
		return -1;
	}

	struct event * ev = malloc(sizeof(struct event));
	if (event_assign(ev, base, fd, EV_READ|EV_PERSIST,
				on_accept, ev) != 0) {
		WRITE_DEBUG_LOG("event_assign failed!!!");
		free(ev);
		close(fd);
		return -1;
	}

	if (event_add(ev, NULL) != 0) {
		WRITE_DEBUG_LOG("event_add failed!!!");
		free(ev);
		close(fd);
		return -1;
	}
	return fd;
}

int main(int argc, char * argv[])
{
	struct event_base * base = event_base_new();

	if (argc >= 2) {
		strncpy(config_file, argv[1], sizeof(config_file));
	} else {
		snprintf(config_file, sizeof(config_file), "%s.cfg", argv[0]);
	}

	if (load_server_config(config_file) != 0) {
		WRITE_ERROR_LOG("load config %s failed\n", config_file);
		return -1;
	}

	char ip[128] = "127.0.0.1";
	int port = 8700;
	int backlog = 10;

	const char * cfg = READ_CONFIG("config", "listen", "ip");
	if (cfg) {
		strncpy(ip, cfg, sizeof(ip));
	}

	int cfg_int = READ_CONFIG_INT("config", "listen", "port");
	if (cfg_int > 0) {
		port = cfg_int;
	}

	cfg_int = READ_CONFIG_INT("config", "listen", "backlog");
	if (cfg_int > 0) {
		backlog = cfg_int;
	}

	if(ev_listen_on(base, ip, port, backlog) < 0) {
		return -1;
	}

	WRITE_INFO_LOG("listen_on %s:%d success", ip, port);

	struct event * ev = evsignal_new(base, SIGINT, on_signal, base);
	evsignal_add(ev, 0);

	ev = evsignal_new(base, SIGINT, on_signal, base);
	evsignal_add(ev, 0);

	ev = evsignal_new(base, SIGTERM, on_signal, base);
	evsignal_add(ev, 0);

	ev = evsignal_new(base, SIGUSR1, on_signal, base);
	evsignal_add(ev, 0);

	ev = evsignal_new(base, SIGUSR2, on_signal, base);
	evsignal_add(ev, 0);

	event_base_dispatch(base);

	event_base_free(base);
	return 0;
}
